package com.serviceprovider.business.module.user.model.impl;

//~--- non-JDK imports --------------------------------------------------------

import com.serviceprovider.business.module.admin.address.model.Address;
import com.serviceprovider.business.module.admin.address.services.IAddressService;
import com.serviceprovider.business.module.admin.role.services.dao.interfaces.IRoleService;
import com.serviceprovider.business.module.admin.settings.services.ISettingsService;
import com.serviceprovider.business.module.admin.user.services.dao.interfaces.IUserService;
import com.serviceprovider.business.module.common.Page;
import com.serviceprovider.business.module.sp.preferences.services.IPreferencesService;
import com.serviceprovider.business.module.user.model.forms.EditUserForm;
import com.serviceprovider.business.module.user.model.forms.ViewUserForm;
import com.serviceprovider.business.module.user.model.interfaces.IElementsSupport;
import com.serviceprovider.business.module.user.model.interfaces.IUserManager;
import com.serviceprovider.business.utility.ApplicationConfigProperties;
import com.serviceprovider.business.utility.SecurityContextUser;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.providers.encoding.PasswordEncoder;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.servlet.ModelAndView;

//~--- JDK imports ------------------------------------------------------------

import java.nio.charset.Charset;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * @author mrduongnv@gmail.com
 */

/**
 * Abstract Implementation of IUserManager. This class implements re-usable
 * methods and adds some utility functions.
 */
public abstract class UserManager implements IUserManager, IElementsSupport {
    protected final Log                   logger = LogFactory.getLog(getClass());
    @Autowired
    protected IAddressService             addressService;
    @Autowired
    protected ApplicationConfigProperties applicationConfigProperties;
    @Autowired
    private PasswordEncoder               passwordEncoder;
    @Autowired
    protected IPreferencesService         preferencesService;
    @Autowired
    protected ISettingsService            propertiesService;
    @Autowired
    protected IRoleService                roleService;
    @Autowired
    protected IUserService                userService;
    @Autowired
    @Qualifier("validator")
    protected Validator                   validator;

    @Override
    public ModelAndView updateUserList(HttpServletRequest request, HttpSession session, ViewUserForm viewUserForm,
                                       BindingResult result) {
        if (result.hasErrors()) {
            ModelAndView mav = new ModelAndView("/users/view_users");

            mav.addObject("elements", getElementsInstance());

            return mav;
        }

        if (viewUserForm.getUsersId() != null) {
            userService.updateUserActiveFlag(viewUserForm.getUsersId(), false);
            userService.updateUserInGuiFlag(viewUserForm.getUsersId(), false);
            userService.unRemove(viewUserForm.getUsersId());
        }

        if (viewUserForm.getUsersListActivated() != null) {
            userService.updateUserActiveFlag(viewUserForm.getUsersListActivated(), true);
        }

        if (viewUserForm.getUsersListDeleted() != null) {
            userService.remove(viewUserForm.getUsersListDeleted());
        }

        if (viewUserForm.getUsersListInGui() != null) {
            userService.updateUserInGuiFlag(viewUserForm.getUsersListInGui(), true);
        }

        return searchUsersPost(request, session, viewUserForm, result);
    }

    @Override
    public ModelAndView enterCreateUser() {
        EditUserForm newUser = new EditUserForm();

        if (SecurityContextUser.isServiceProvider()) {
            newUser.setLevel1User(userService.getUserConnected());
            newUser.setLevel1UserId(userService.getUserConnected().getId());
        }

        ModelAndView mav = new ModelAndView("/users/create_user");

        mav.addObject("level1Users", userService.findAllLevel1Users());

        EditUserForm editForm = new EditUserForm();

        editForm.setCurrentUser(userService.getUserConnected());
        editForm.setFee(propertiesService.getDefaultFee());
        editForm.setCheckPrintingBy(propertiesService.getDefaultCheckPrintedBy());
        editForm.setOrderApproval(propertiesService.getDefaultOrderApproval());
        editForm.setRoleId(2);    // Create Service Providers by default.
        mav.addObject("userForm", editForm);
        mav.addObject("elements", getElementsInstance());

        return mav;
    }

    @Override
    public ModelAndView updateUserDetails(EditUserForm editUserForm, BindingResult result) {
        validator.validate(editUserForm, result);
        validateAddresses(result, editUserForm.getContactAddress(), "contactAddress");
        validateAddresses(result, editUserForm.getBusinessAddress(), "businessAddress");
        validateAddresses(result, editUserForm.getShippingAddress(), "shippingAddress");

        if (result.hasErrors()) {
            ModelAndView mav = new ModelAndView("/users/edit_user_details");

            mav.addObject("userForm", editUserForm);
            mav.addObject("level1Users", userService.findAll());
            mav.addObject("elements", getElementsInstance());

            return mav;
        }

        User   user     = saveUserDetails(editUserForm);
        String redirect = "redirect:/users/view_user_details.htm?saveSuccess=true&id=" + user.getId();

        return new ModelAndView(redirect);
    }

    @Override
    public ModelAndView viewMyUserDetails() {
        return null;
    }

    @Override
    public ModelAndView editMyUserDetails(HttpServletRequest request) {
        return null;
    }

    protected ModelAndView searchUsers(HttpServletRequest request, UserSearchCriteria criteria,
                                       ViewUserForm viewUserForm, BindingResult result) {
        ModelAndView mav = new ModelAndView("/users/view_users");

        if (result != null) {
            validator.validate(viewUserForm, result);

            if (result.hasErrors()) {
                mav.addObject("viewUserForm", viewUserForm);
                mav.addObject("elements", getElementsInstance());

                return mav;
            }
        }

        int recordSize = applicationConfigProperties.getRecordSize("view_users.records.size");

        if ((request.getParameter("recordSize") != null) &&!"0".equals(request.getParameter("recordSize"))) {
            recordSize = Integer.parseInt(request.getParameter("recordSize"));
        }

        viewUserForm.setRecordSize(recordSize);

        if (viewUserForm.getSortOrder() == null) {
            viewUserForm.setSortOrder("desc");
        }

        if (viewUserForm.getSortColumn() == null) {
            viewUserForm.setSortColumn("id");
        }

        if (viewUserForm.getPageNumber() == null) {
            criteria.setPageNo(1);
        } else {
            criteria.setPageNo(viewUserForm.getPageNumber());
        }

        if ((request.getParameter("newSearch") != null) && Boolean.parseBoolean(request.getParameter("newSearch"))) {
            criteria.setEmail(viewUserForm.getEmail());
            criteria.setFirstName(viewUserForm.getFirstName());
            criteria.setLastName(viewUserForm.getLastName());
            criteria.setEfin(viewUserForm.getEfin());
            criteria.setSsn(viewUserForm.getSsn());
            criteria.setPtin(viewUserForm.getPtin());
            criteria.setCity(viewUserForm.getCity());
            criteria.setCountry(viewUserForm.getCountry());
            criteria.setZip(viewUserForm.getZip());
            criteria.setRoleId(viewUserForm.getRoleId());
            criteria.setBusinessName(viewUserForm.getBusinessName());
            criteria.setRefNumber(viewUserForm.getRefNumber());
        } else {

            // ViewUserForm is set again only to display each filter's field
            // with its corresponding value
            viewUserForm.setEmail(criteria.getEmail());
            viewUserForm.setFirstName(criteria.getFirstName());
            viewUserForm.setLastName(criteria.getLastName());
            viewUserForm.setEfin(criteria.getEfin());
            viewUserForm.setSsn(criteria.getSsn());
            viewUserForm.setCity(criteria.getCity());
            viewUserForm.setCountry(criteria.getCountry());
            viewUserForm.setZip(criteria.getZip());
            viewUserForm.setRoleId(criteria.getRoleId());
            viewUserForm.setPtin(criteria.getPtin());
            viewUserForm.setBusinessName(criteria.getBusinessName());
            viewUserForm.setLevel1UserId(criteria.getLevel1UserID());
            viewUserForm.setRefNumber(criteria.getRefNumber());
        }

        Page<User> userList = userService.findUserByCriteria(criteria, (viewUserForm.getPageNumber() == null)
                ? 1
                : viewUserForm.getPageNumber(), recordSize, viewUserForm.getSortColumn(), viewUserForm.getSortOrder());

        userService.retrieveLevel1User(userList.getPageItems());
        roleService.retrieveRole(userList.getPageItems());

        int inGui = 0;

        for (User u : userList.getPageItems()) {
            if (u.getInGui()) {
                inGui++;
            }

            if (u.getLevel1User() == null) {
                User emptyUser = new User();

                emptyUser.setId(0);
                u.setLevel1User(emptyUser);
            }
        }

        viewUserForm.setInGuiTotal(inGui);
        viewUserForm.setUsersList(userList.getPageItems());
        viewUserForm.setPagesAvailable(userList.getPagesAvailable());
        viewUserForm.setTotalRecords(userList.getTotalRecords());
        mav.addObject("viewUserForm", viewUserForm);
        mav.addObject("elements", getElementsInstance());

        return mav;
    }

    /**
     * UserManager's utility function to retrieve and User. Retrieves an User
     * and its Level1User and Role properties.
     *
     * @param userId
     * @return User
     */
    protected User getUserDetailed(int userId) {
        User user = userService.getById(userId);

        userService.retrieveLevel1User(user);
        roleService.retrieveRole(user);
        addressService.retrieveAddresses(user);

        return user;
    }

    /**
     * UserManager's utility function to validate and Address object. Address
     * validation. Validates mandatory fields.
     *
     * @param result
     * @param formAddress
     * @param field
     */
    protected void validateAddresses(BindingResult result, Address formAddress, String field) {
        String streetAddress1 = formAddress.getStreetAddress1().trim();
        String city           = formAddress.getCity().trim();
        String country        = formAddress.getCountry().getCode().trim();
        String state          = formAddress.getState().getCode().trim();

        if (streetAddress1.equals("") && city.equals("") && country.equals("")) {
            return;
        }

        if (streetAddress1.equals("")) {
            result.rejectValue(field + ".streetAddress1", field + ".streetAddress1");
        }

        if (city.equals("")) {
            result.rejectValue(field + ".city", field + ".city");
        }

        if (country.equals("")) {
            result.rejectValue(field + ".country", field + ".country");
        }

        if (state.equals("")) {
            result.rejectValue(field + ".state", field + "state");
        }
    }

    protected void validateEmailEfin(EditUserForm editUserForm, BindingResult result) {

        // Check email to be unique
        UserSearchCriteria checkCriteria = new UserSearchCriteria();

        checkCriteria.setEmail(editUserForm.getEmail());

        Page<User> checkUsers = userService.findUserByCriteria(checkCriteria, 1, 1, "email", "desc");

        if (checkUsers.getTotalRecords() > 0) {
            result.rejectValue("email", "email.already.exists");
        }

        checkCriteria.setEmail(null);

        if (editUserForm.getEfin() != null) {
            checkCriteria.setEfin(editUserForm.getEfin());
            checkUsers = userService.findUserByCriteria(checkCriteria, 1, 1, "efin", "desc");

            if (checkUsers.getTotalRecords() > 0) {
                result.rejectValue("efin", "efin.already.exists");
            }
        }
    }

    /**
     * UserManager's utility function to get an Address. Copy address values
     * from Form to User's address
     *
     * @param userAddress
     * @param formAddress
     */
    protected Address getAddressFromUserForm(Address userAddress, Address formAddress) {
        String city           = formAddress.getCity();
        String streetAddress1 = formAddress.getStreetAddress1();
        String country        = formAddress.getCountry().getCode().trim();

        if (streetAddress1.equals("") && city.equals("") && country.equals("")) {
            return null;    // There's no address to update or create
        }

        if (userAddress == null) {
            userAddress = new Address();
        }

        userAddress.setStreetAddress1(formAddress.getStreetAddress1());
        userAddress.setStreetAddress2(formAddress.getStreetAddress2());
        userAddress.setCity(formAddress.getCity());
        userAddress.setState(formAddress.getState());
        userAddress.setCountry(formAddress.getCountry());
        userAddress.setZip(formAddress.getZip());
        userAddress.setPhone(formAddress.getPhone());
        userAddress.setFax(formAddress.getFax());
        userAddress.setCell(formAddress.getCell());

        return userAddress;
    }

    /**
     * UserManager's utility function to create a new User. Creates a new user
     * from the form properties values.
     *
     * @param editUserForm to create User object from
     * @return User
     */
    protected User createUser(EditUserForm editUserForm) {
        User newUser = new User();

        newUser.setFirstName(editUserForm.getFirstName());
        newUser.setMilddleName(editUserForm.getMilddleName());
        newUser.setLastName(editUserForm.getLastName());
        newUser.setEmail(editUserForm.getEmail());

        // encode password
        newUser.setPassword(passwordEncoder.encodePassword(editUserForm.getPassword(), null));
        newUser.setConfirmPassword(editUserForm.getConfirmPassword());
        newUser.setEfin(editUserForm.getEfin());
        newUser.setBusinessFein(editUserForm.getBusinessFein());
        newUser.setOwnerSsn(editUserForm.getOwnerSsn());
        newUser.setCheckPrintingBy(editUserForm.getCheckPrintingBy());
        newUser.setOrderApproval(editUserForm.getOrderApproval());
        newUser.setEnabled(editUserForm.isEnabled());
        newUser.setBusinessName(editUserForm.getBusinessName());
        newUser.setRole(roleService.getRoleById(editUserForm.getRoleId()));
        newUser.setLevel1User(userService.getById(editUserForm.getLevel1UserId()));
        userService.save(newUser);

        return newUser;
    }

    /**
     * UserManager's utility function to save an User. Updates an user taking
     * the values from the Form.
     *
     * @param editUserForm
     * @return User
     */
    protected User saveUserDetails(EditUserForm editUserForm) {
        User user = userService.getById(editUserForm.getId());

        addressService.retrieveAddresses(user);
        user.setEmail(editUserForm.getEmail());
        user.setAlternateEmail(editUserForm.getAlternateEmail());

        // encode password
        user.setPassword(passwordEncoder.encodePassword(editUserForm.getPassword(), null));
        user.setFirstName(editUserForm.getFirstName());
        user.setMilddleName(editUserForm.getMilddleName());
        user.setLastName(editUserForm.getLastName());
        user.setEfin(editUserForm.getEfin());
        user.setSsn(editUserForm.getSsn());
        user.setPtin(editUserForm.getPtin());
        user.setCheckPrintingBy(editUserForm.getCheckPrintingBy());
        user.setOrderApproval(editUserForm.getOrderApproval());
        user.setBusinessName(editUserForm.getBusinessName());
        user.setBusinessFein(editUserForm.getBusinessFein());
        user.setOwnerSsn(editUserForm.getOwnerSsn());
        user.setPrefMessageNotifyType(editUserForm.getPrefMessageNotifyType());
        user.setPrefMessageNotifyAlert(editUserForm.getPrefMessageNotifyAlert());
        user.setLevel1User(userService.getById(editUserForm.getLevel1User().getId()));
        user.setRole(roleService.getRoleById(editUserForm.getRole().getId()));

        // Contact Address
        user.setContactAddress(getAddressFromUserForm(user.getContactAddress(), editUserForm.getContactAddress()));

        // Business Address
        user.setBusinessAddress(getAddressFromUserForm(user.getBusinessAddress(), editUserForm.getBusinessAddress()));

        // Shipping Address
        user.setShippingAddress(getAddressFromUserForm(user.getShippingAddress(), editUserForm.getShippingAddress()));
        userService.update(user);

        return user;
    }
}



